package com.fp.system;

import com.fp.system.constants.CronConstants;
import com.fp.system.expression.*;
import org.quartz.CronExpression;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * cron表达式操作
 * <p>
 * Created by haibiao.zhang on 17/1/5.
 */
public class CronOperator {

    private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 解析cronExpressionDto实体
     *
     * @param cronExpressionDto
     * @return
     */
    private synchronized List<String> parseCron(CronExpressionDto cronExpressionDto) {
        if (cronExpressionDto == null) {
            return null;
        }
        List<String> cronList = new ArrayList<>();
        StringBuilder cronExpression = new StringBuilder();
        StringBuilder startCronExpression = new StringBuilder();
        StringBuilder endCronExpression = new StringBuilder();

        SecondCronExpression seconds = cronExpressionDto.getSeconds();
        MinuteCronExpression minutes = cronExpressionDto.getMinutes();
        HourCronExpression hours = cronExpressionDto.getHours();
        DayCronExpression days = cronExpressionDto.getDay();
        MonthCronExpression months = cronExpressionDto.getMonth();
        WeekCronExpression weeks = cronExpressionDto.getWeek();
        YearCronExpression year = cronExpressionDto.getYear();

        //判断日期和星期是否符合互斥规则
        if (days.getRandom() && weeks.getRandom() || !days.getRandom() && !weeks.getRandom()) {
            throw new RuntimeException("日期和星期的设置不符合Spring中cron表达式的互斥规则!");
        }

        //判断分钟和小时是否符合规范
        if (minutes.getRange() && hours.getRange()) {
            throw new RuntimeException("cron表达式中分钟和小时的设置不符合规范!");
        }

        //秒钟解析
        parseCommonCron(cronExpression, startCronExpression, endCronExpression, seconds);
        cronExpression.append(" ");
        startCronExpression.append(" ");
        endCronExpression.append(" ");

        //分钟解析
        parseCommonCron(cronExpression, startCronExpression, endCronExpression, minutes);
        cronExpression.append(" ");
        startCronExpression.append(" ");
        endCronExpression.append(" ");

        //小时解析
        parseCommonCron(cronExpression, startCronExpression, endCronExpression, hours);
        cronExpression.append(" ");
        startCronExpression.append(" ");
        endCronExpression.append(" ");

        //日期解析
        if (days.getLast()) {
            cronExpression.append(CronConstants.DAY.D35.getStrDay());
            startCronExpression.append(CronConstants.DAY.D35.getStrDay());
            endCronExpression.append(CronConstants.DAY.D35.getStrDay());
        } else if (days.getRandom()) {
            cronExpression.append(CronConstants.DAY.D36.getStrDay());
            startCronExpression.append(CronConstants.DAY.D36.getStrDay());
            endCronExpression.append(CronConstants.DAY.D36.getStrDay());
        } else {
            parseCommonCron(cronExpression, startCronExpression, endCronExpression, days);
        }
        cronExpression.append(" ");
        startCronExpression.append(" ");
        endCronExpression.append(" ");

        //月份解析
        parseCommonCron(cronExpression, startCronExpression, endCronExpression, months);
        cronExpression.append(" ");
        startCronExpression.append(" ");
        endCronExpression.append(" ");

        //星期解析
        if (weeks.getRandom()) {
            cronExpression.append(CronConstants.WEEK.W12.getStrWeek());
            startCronExpression.append(CronConstants.WEEK.W12.getStrWeek());
            endCronExpression.append(CronConstants.WEEK.W12.getStrWeek());
        } else if (weeks.getLast()) {
            cronExpression.append(weeks.getLastWeek()).append(CronConstants.WEEK.W13.getStrWeek());
            startCronExpression.append(weeks.getLastWeek()).append(CronConstants.WEEK.W13.getStrWeek());
            endCronExpression.append(weeks.getLastWeek()).append(CronConstants.WEEK.W13.getStrWeek());
        } else if (weeks.getOrderWeek()) {
            cronExpression.append(weeks.getOrderWeekList().get(0)).append(CronConstants.WEEK.W11.getStrWeek()).append(weeks.getOrderWeekList().get(1));
            startCronExpression.append(weeks.getOrderWeekList().get(0)).append(CronConstants.WEEK.W11.getStrWeek()).append(weeks.getOrderWeekList().get(1));
            endCronExpression.append(weeks.getOrderWeekList().get(0)).append(CronConstants.WEEK.W11.getStrWeek()).append(weeks.getOrderWeekList().get(1));
        } else {
            parseCommonCron(cronExpression, startCronExpression, endCronExpression, weeks);
        }
        cronExpression.append(" ");
        startCronExpression.append(" ");
        endCronExpression.append(" ");

        //年份解析
        parseCommonCron(cronExpression, startCronExpression, endCronExpression, year);

        cronList.add(cronExpression.toString());
        cronList.add(startCronExpression.toString());
        cronList.add(endCronExpression.toString());
        return cronList;
    }

    /**
     * 解析相同的cron表达式
     *
     * @param cronExpression
     * @param startCronExpression
     * @param endCronExpression
     * @param abstractCronExpression
     */
    private synchronized void parseCommonCron(StringBuilder cronExpression, StringBuilder startCronExpression, StringBuilder endCronExpression, AbstractCronExpression abstractCronExpression) {
        if (abstractCronExpression != null) {
            if (abstractCronExpression.getAssign() && abstractCronExpression.getAssignList() != null) {
                int size = abstractCronExpression.getAssignList().size();
                int i = 0;
                for (String assign : abstractCronExpression.getAssignList()) {
                    if (size == 1) {
                        cronExpression.append(assign);
                        startCronExpression.append(assign);
                        endCronExpression.append(assign);
                        break;
                    }
                    if (i < size - 1) {
                        cronExpression.append(assign + CronConstants.YEAR.Y3.getStrYear());
                        startCronExpression.append(assign + CronConstants.YEAR.Y3.getStrYear());
                        endCronExpression.append(assign + CronConstants.YEAR.Y3.getStrYear());
                    } else {
                        cronExpression.append(assign);
                        startCronExpression.append(assign);
                        endCronExpression.append(assign);
                    }
                    i++;
                }
            } else if (!abstractCronExpression.getAssign()) {
                cronExpression.append(CronConstants.YEAR.Y1.getStrYear());
                startCronExpression.append(CronConstants.YEAR.Y1.getStrYear());
                endCronExpression.append(CronConstants.YEAR.Y1.getStrYear());
            }
            if (abstractCronExpression.getRange() != null) {
                if (abstractCronExpression.getRange() && abstractCronExpression.getRangeStart() != null && abstractCronExpression.getRangeEnd() != null) {
                    cronExpression.append(abstractCronExpression.getRangeStart()).append(CronConstants.YEAR.Y2.getStrYear()).append(abstractCronExpression.getRangeEnd());
                    if (abstractCronExpression instanceof MinuteCronExpression || abstractCronExpression instanceof HourCronExpression) {
                        startCronExpression.append(abstractCronExpression.getRangeStart());
                        endCronExpression.append(abstractCronExpression.getRangeEnd());
                    } else {
                        startCronExpression.append(abstractCronExpression.getRangeStart()).append(CronConstants.YEAR.Y2.getStrYear()).append(abstractCronExpression.getRangeEnd());
                        endCronExpression.append(abstractCronExpression.getRangeStart()).append(CronConstants.YEAR.Y2.getStrYear()).append(abstractCronExpression.getRangeEnd());
                    }
                }
            }
        } else {
            cronExpression.append(CronConstants.YEAR.Y1.getStrYear());
            startCronExpression.append(CronConstants.YEAR.Y1.getStrYear());
            endCronExpression.append(CronConstants.YEAR.Y1.getStrYear());
        }
    }

    /**
     * 判断当前时间是否有效
     *
     * @param cronExpressionDto
     * @return
     * @throws ParseException
     */
    private synchronized boolean judgeEffective(CronExpressionDto cronExpressionDto) throws ParseException {
        List<String> expression = parseCron(cronExpressionDto);
        String startTimeCronExpression = expression.get(1);
        String endTimeCronExpression = expression.get(2);
        if (startTimeCronExpression == null || endTimeCronExpression == null) {
            return false;
        }
        Date date = new Date();
        CronExpression cronExpression1 = new CronExpression(startTimeCronExpression);
        CronExpression cronExpression2 = new CronExpression(endTimeCronExpression);
        Date nextStartDate = cronExpression1.getNextValidTimeAfter(date);
        if (nextStartDate == null) {
            return false;
        }
        Date nextEndDate = cronExpression2.getNextValidTimeAfter(nextStartDate);
        if (nextEndDate == null) {
            return false;
        }
        Date realStopTime = cronExpression2.getNextValidTimeAfter(date);
        Date realStartTime = new Date(realStopTime.getTime() - (nextEndDate.getTime() - nextStartDate.getTime()));
        if (realStartTime.getTime() <= date.getTime() && realStopTime.getTime() >= date.getTime()) {
            return true;
        } else {
            return false;
        }
    }

    private String[] startFormatTime;
    private String[] endFormatTime;
    private String[] startDetailTime;
    private String[] endDetailTime;

    private Calendar calendar;
    private Integer currentYear;
    private Integer currentMonth;
    private Integer currentDay;
    private Integer currentHour;
    private Integer currentMinute;

    private boolean yearFlag;
    private String dayStart = null;
    private String dayEnd = null;
    private String minuteStart = null;
    private String minuteEnd = null;

    /**
     * 判断过滤实体的参数
     *
     * @param filterTargetPo
     * @param calDate
     * @return
     */
    private synchronized CronExpressionDto judgeFilterTargetPo(FilterTargetPo filterTargetPo, Date calDate) {
        if (filterTargetPo.getDetailStartTime() == null || filterTargetPo.getDetailEndTime() == null) {
            return null;
        }
        return generateCronExpressionDto(filterTargetPo, calDate);
    }

    /**
     * 生成CronExpressionDto实体
     *
     * @param filterTargetPo
     * @param calDate
     * @return
     */
    private synchronized CronExpressionDto generateCronExpressionDto(FilterTargetPo filterTargetPo, Date calDate) {

        calendar = Calendar.getInstance();
        calendar.setTime(calDate);
        currentYear = calendar.get(Calendar.YEAR);
        currentMonth = calendar.get(Calendar.MONTH) + 1;
        currentDay = calendar.get(Calendar.DAY_OF_MONTH);
        currentHour = calendar.get(Calendar.HOUR_OF_DAY);
        currentMinute = calendar.get(Calendar.MINUTE);
        yearFlag = false;

        CronExpressionDto cronExpressionDto = new CronExpressionDto();
        YearCronExpression year = new YearCronExpression();
        WeekCronExpression week = new WeekCronExpression();
        MonthCronExpression month = new MonthCronExpression();
        DayCronExpression day = new DayCronExpression();
        HourCronExpression hour = new HourCronExpression();
        MinuteCronExpression minute = new MinuteCronExpression();
        SecondCronExpression second = new SecondCronExpression();

        startFormatTime = formatTime(filterTargetPo.getStartTime());
        endFormatTime = formatTime(filterTargetPo.getEndTime());
        if (filterTargetPo.getDetailStartTime() != null && filterTargetPo.getDetailEndTime() != null) {
            startDetailTime = filterTargetPo.getDetailStartTime().split(":");
            endDetailTime = filterTargetPo.getDetailEndTime().split(":");
        }

        //年设置
        int startYear = Integer.parseInt(startFormatTime[0]);
        int endYear = Integer.parseInt(endFormatTime[0]);
        year.setAssign(true);
        if (startYear == endYear) {
            yearFlag = true;
        }
        boolean between = false;
        boolean equal = false;
        boolean less = false;
        boolean large = false;
        if (currentYear > startYear && currentYear < endYear) {
            year.setAssignList(Arrays.asList(String.valueOf(currentYear)));
            between = true;
        } else if (currentYear <= startYear) {
            year.setAssignList(Arrays.asList(String.valueOf(startYear)));
            less = true;
        } else if (currentYear >= endYear) {
            year.setAssignList(Arrays.asList(String.valueOf(endYear)));
            large = true;
        } else {
            year.setAssignList(Arrays.asList(String.valueOf(currentYear)));
            equal = true;
        }

        //星期设置
        week.setAssign(true);
        week.setAssignList(filterTargetPo.getWeekSelectedList());

        //月份设置
        int startMonth = Integer.parseInt(startFormatTime[1]);
        int endMonth = Integer.parseInt(endFormatTime[1]);
        month.setAssign(true);
        if (startMonth == endMonth && yearFlag) {
            month.setAssignList(Arrays.asList(String.valueOf(startMonth)));
        } else {
            month.setRange(true);
            if (between) {
                month.setRangeStart(String.valueOf(1));
                month.setRangeEnd(String.valueOf(12));
            }
            if (less) {
                month.setRangeStart(String.valueOf(startMonth));
                month.setRangeEnd(String.valueOf(12));
            }
            if (large) {
                month.setRangeStart(String.valueOf(1));
                month.setRangeEnd(String.valueOf(endMonth));
            }
            if (equal) {
                month.setRangeStart(String.valueOf(startMonth));
                month.setRangeEnd(String.valueOf(endMonth));
            }
        }

        //日期设置
        day.setRandom(true);
        dayStart = startFormatTime[2];
        dayEnd = endFormatTime[2];

        //时刻设置
        if (startDetailTime != null && endDetailTime != null) {
            hour.setAssign(true);
            if (Integer.parseInt(endDetailTime[0]) - Integer.parseInt(startDetailTime[0]) == 1) {
                if (Integer.parseInt(startDetailTime[0]) == currentHour) {
                    hour.setAssignList(Arrays.asList(String.valueOf(currentHour)));
                } else {
                    hour.setAssignList(Arrays.asList(String.valueOf(Integer.parseInt(endDetailTime[0]))));
                }
            } else {
                if (startDetailTime[0].equals(endDetailTime[0])) {
                    hour.setAssignList(Arrays.asList(String.valueOf(Integer.parseInt(startDetailTime[0]))));
                } else {
                    hour.setRange(true);
                    if (currentHour >= Integer.parseInt(startDetailTime[0]) && currentHour <= Integer.parseInt(endDetailTime[0])) {
                        hour.setRangeStart(String.valueOf(currentHour));
                        hour.setRangeEnd(String.valueOf(currentHour));
                    } else {
                        hour.setRangeStart(startDetailTime[0]);
                        hour.setRangeEnd(endDetailTime[0]);
                    }
                }
            }

            //分钟设置
            minuteStart = startDetailTime[1];
            minuteEnd = endDetailTime[1];
        }

        cronExpressionDto.setSeconds(second);
        cronExpressionDto.setMinutes(minute);
        cronExpressionDto.setHours(hour);
        cronExpressionDto.setDay(day);
        cronExpressionDto.setMonth(month);
        cronExpressionDto.setWeek(week);
        cronExpressionDto.setYear(year);
        return cronExpressionDto;
    }

    /**
     * 格式化时间
     *
     * @param date
     * @return
     */
    private synchronized String[] formatTime(Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        return simpleDateFormat.format(date).split("-");
    }

    /**
     * 判断当前日期
     *
     * @return
     */
    private synchronized boolean judegeCurrentDay() {
        if (yearFlag) {
            if (currentMonth == Integer.parseInt(startFormatTime[1])) {
                if (currentDay >= Integer.parseInt(dayStart)) {
                    return true;
                } else {
                    return false;
                }
            } else if (currentMonth == Integer.parseInt(endFormatTime[1])) {
                if (currentDay <= Integer.parseInt(dayEnd)) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return true;
            }
        } else {
            if (currentYear == Integer.parseInt(startFormatTime[0])) {
                if (currentMonth == Integer.parseInt(startFormatTime[1])) {
                    if (currentDay >= Integer.parseInt(dayStart)) {
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    return true;
                }
            } else if (currentYear == Integer.parseInt(endFormatTime[0])) {
                if (currentMonth == Integer.parseInt(endFormatTime[1])) {
                    if (currentDay > Integer.parseInt(dayEnd)) {
                        return false;
                    } else {
                        return true;
                    }
                } else {
                    return true;
                }
            } else {
                return true;
            }
        }
    }

    /**
     * 判断当前分钟
     *
     * @return
     */
    private synchronized boolean judgeCurrentMinute() {
        StringBuilder start = new StringBuilder();
        StringBuilder current = new StringBuilder();
        StringBuilder end = new StringBuilder();
        for (int i = 0; i <= 1; i++) {
            if (startDetailTime[i].length() == 1) {
                start.append("0");
            }
            start.append(startDetailTime[i]);
            if (i == 0) {
                if(currentHour<10){
                    current.append("0");
                }
                current.append(currentHour);
            } else {
                if(currentMinute<10){
                    current.append("0");
                }
                current.append(currentMinute);
            }
            if (endDetailTime[i].length() == 1) {
                end.append("0");
            }
            end.append(endDetailTime[i]);
        }
        if (start.toString().compareTo(current.toString()) <= 0 && end.toString().compareTo(current.toString()) >= 0) {
            return true;
        }
        return false;
    }

    /**
     * 过滤不符合当前时间的促销
     *
     * @param filterTargetPo
     * @param calDate
     * @return
     */
    public synchronized String filter(FilterTargetPo filterTargetPo, Date calDate) throws ParseException {
        CronExpressionDto cronExpressionDto = judgeFilterTargetPo(filterTargetPo, calDate);
        if (cronExpressionDto != null) {
            if (judgeEffective(cronExpressionDto) && judegeCurrentDay() && judgeCurrentMinute()) {
                return CronConstants.STATUS.S1.getReturnMsg();
            } else {
                return CronConstants.STATUS.S2.getReturnMsg();
            }
        } else {
            return CronConstants.STATUS.S3.getReturnMsg();
        }
    }

    /**
     * 过滤非当日的活动
     *
     * @param filterTargetPo
     * @param calDate
     * @return
     * @throws ParseException
     */
    public synchronized boolean filterNoIntradayActivity(FilterTargetPo filterTargetPo, Date calDate) throws ParseException {
        if (filterTargetPo.getDetailStartTime() == null && filterTargetPo.getDetailEndTime() == null) {
            //没有具体时间的活动处理
            Calendar currentTime = Calendar.getInstance();
            currentTime.setTime(calDate);
            Long currents = getTimes(format.format(currentTime.getTime()));
            Long starts = getTimes(format.format(filterTargetPo.getStartTime()));
            Long ends = getTimes(format.format(filterTargetPo.getEndTime()));
            if (starts <= currents && currents <= ends) {
                return true;
            } else {
                return false;
            }
        } else {
            filterTargetPo.setDetailStartTime(null);
            filterTargetPo.setDetailEndTime(null);
            CronExpressionDto cronExpressionDto = generateCronExpressionDto(filterTargetPo, calDate);
            CronExpression cronExpression = new CronExpression(parseCron(cronExpressionDto).get(0));
            Date date = cronExpression.getNextValidTimeAfter(calendar.getTime());
            if (date != null) {
                if (format.format(calendar.getTime()).equals(format.format(date))) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }
    }

    /**
     * 获取时间
     *
     * @param date
     * @return
     */
    private synchronized Long getTimes(String date) throws ParseException {
        return format.parse(date).getTime();
    }

}